/***********************************************
 This file is part of redia-bapps-main-ios.
 
 Copyright (c) 2015 Redia A/S
 
 Redia-bapps-main-ios is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Redia-bapps-main-ios is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with redia-bapps-main-ios.  If not, see <http://www.gnu.org/licenses/>.
 
 *********************************************** */

//
//  BAFetchHelper.m
//  redia-bapps-ios
//
//  Created by Nikolaj Tørring on 8/2/13.
//  Copyright (c) 2013 redia. All rights reserved.
//

#import "BARPCHelper.h"
#import "BARPCManager.h"
#import "JSONRPCClient.h"
#import "BALibrary.h"
#import "BASearchResult.h"
#import "BAFullRecord.h"
#import "JSONEncodingHelperMethods.h"
#import "BABranch.h"
#import "BAReservation.h"
#import "BALoan.h"
#import "BACoverUrl.h"
#import "BAOpeningHoursLibrary.h"
#import "BAAboutCreator.h"
#import "BARecord.h"
#import "BAReview.h"
#import "BANews.h"
#import "BAEvent.h"
#import "BAPopular.h"
#import "JSONRPCRequestHelper.h"
#import "BASupportInfo.h"

#ifdef BA_LIBRARIAN_BUILD
#import "LBHoldingItem.h"
#import "LBHoldingItemHistoryRecord.h"
#import "LBAutoCompletionListContainer.h"
#endif

#ifdef BA_IMF_BUILD
#import "IMFAbstractPackage.h"
#import "IMFPackage.h"
#import "IMFPackageListResult.h"
#endif


@implementation BARPCHelper

/*
-(void)dealloc{
    DLog(@"dealloc");
}
*/


#pragma mark JSONRPCClientDelegate
- (void)JSONRPCClient:(JSONRPCClient *)client requestSucceeded:(NSInteger)requestId result:(id)result
{
    
    NSArray* clientOrigRequests = [client getRequests];
    NSURL* requestServerUrl=nil;
    if ([clientOrigRequests count]==1) {
        //only provide serverurl if we can guarantee which one it was
        NSURLConnection* urlreq = [clientOrigRequests firstObject];
        requestServerUrl = [[urlreq originalRequest] URL];
    }
    
    [BARPCManager removeConnection:client];
    
    //DLog(@"result: %@",result);
    
    /* Verify that value for "result" key is true.
        This duplicates parsing of the same object structure as in BAStandardResultObject, but we need more
        deailed error handling here, in order to correctly call the code blocks provided by the caller.
     */
    if (![JSONEncodingHelperMethods parseBoolWithKey:@"result" fromJsonObject:result]) {
        NSString* server_message = [JSONEncodingHelperMethods parseStringWithKey:@"message" fromJsonObject:result];
        if (server_message==nil) {
            NSString* invalid_error_message = @"Error: invalid response; error response without error message";
            DLog(@"%@",invalid_error_message);
            self.errorBlock(BA_RPC_ERROR_INVALID_SERVER_RESPONSE, invalid_error_message);
            return;
        }
        NSInteger code = [JSONEncodingHelperMethods parseIntegerWithKey:@"code" fromJsonObject:result];
        
#ifdef DEBUG
        NSString* errormessage = [NSString stringWithFormat:@"Error message from server: code %d: %@",code, server_message];
        DLog(@"Error: %@", errormessage);
#endif
        self.errorBlock(code, server_message);
        return;
    }
    
    switch (requestId) {
            
        case BA_REQID_GET_LIBRARIES:
        {
            DLog(@"BA_REQID_GET_LIBRARIES");
            //DLog(@"result: %@",result);
            
            NSArray *libraries;
            @try {
                libraries = [JSONEncodingHelperMethods parseArrayOfClass:[BALibrary class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object: %@",exception]);
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(libraries);
                
            }
            
            break;
        }
        case BA_REQID_SEARCH:
        {
            DLog(@"BA_REQID_SEARCH");
            //DLog(@"result: %@",result);

            BASearchResult* searchResult;
            @try {
                searchResult = [BASearchResult objectWithJSONObject:[result objectForKey:@"data"]];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(searchResult);
                
            }
            break;
        }
            
        case BA_REQID_GET_OBJECTS:
        {
            DLog(@"BA_REQID_GET_OBJECTS");
            //DLog(@"result: %@",result);

            NSMutableDictionary* fullRecordDict = [NSMutableDictionary new];
            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                for (NSString* key in [data allKeys]) {
                    BAStandardResultObject* standard_result = [BAStandardResultObject objectWithJSONObject:[data objectForKey:key]];
                    BOOL parse_result = [standard_result parseDataFieldToObjectOfClass:[BAFullRecord class]]; //we know that the data field will contain a Full Record, if result is true
                    if (!parse_result) {
                        DLog(@"warning: error in full record for id %@: %d %@", key, standard_result.code, standard_result.message);
                    }
                    [fullRecordDict setObject:standard_result forKey:key];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock([NSDictionary dictionaryWithDictionary:fullRecordDict]);
                
            }
            break;
        }
            
        case BA_REQID_VERSION:
        {
            DLog(@"BA_REQID_VERSION");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }

        case BA_REQID_AUTHENTICATE:
        {
            DLog(@"BA_REQID_AUTHENTICATE");
            DLog(@"result: %@",result);
            
            //TODO: This will be redundant, as the sessionID will be moved to a cookie.
            NSString* sessionID = [result objectForKey:@"session"];
            if ([sessionID length]>0) {
                [BARPCManager instance].currentPHPSessionID = sessionID;
            } else {
                [NSException raise:@"authenticate response didn't contain session id" format:@"can't authenticate without a specific phpsessid"];
            }
            
            
            if (requestServerUrl) {
                
                NSHTTPCookieStorage* cs = [NSHTTPCookieStorage sharedHTTPCookieStorage];
                NSArray* cookies = [cs cookiesForURL:requestServerUrl].copy;

                NSMutableDictionary* cookiesDict = [NSMutableDictionary new];
                
                for (NSString* cycleurl in [BARPCManager instance].serverUrlStrings) {
                    NSMutableArray* cookiesForUrl = [NSMutableArray new];
                    
                    NSURL* serverUrlUrl = [NSURL URLWithString:cycleurl];
                    
                    for (NSHTTPCookie* cookie in cookies) {
                        NSMutableDictionary* properties = cookie.properties.mutableCopy;
                        [properties setObject:serverUrlUrl.host forKey:NSHTTPCookieDomain];
                        NSHTTPCookie* newCookie = [NSHTTPCookie cookieWithProperties:properties];
                        if (newCookie != nil) {
                            [cookiesForUrl addObject:newCookie];
                        }
                        
                    }
                    [cookiesDict setObject:cookiesForUrl.copy forKey:cycleurl];
                }
                [BARPCManager instance].cookiesDict = cookiesDict.copy;


            } else {
                NSLog(@"Error: request url not associable with authenticate");
            }

            

            NSDictionary* data = [result objectForKey:@"data"];
            BAPatron* resultPatron;
            @try {
                resultPatron = [BAPatron objectWithJSONObject:data];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(resultPatron);
            }
            break;
        }

        case BA_REQID_DEAUTHENTICATE:
        {
            DLog(@"BA_REQID_DEAUTHENTICATE");
            //DLog(@"result: %@",result);

            
            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
                
                
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager invalidateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }

        case BA_REQID_IS_AUTHENTICATED:
        {
            DLog(@"BA_REQID_IS_AUTHENTICATED");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }

        case BA_REQID_GET_BRANCHES:
        {
            DLog(@"BA_REQID_GET_BRANCHES");
            //DLog(@"result: %@",result);

            NSArray* resultArray;
            @try {
                resultArray = [JSONEncodingHelperMethods parseArrayOfClass:[BABranch class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(resultArray); 
                
            }
            break;
        }

        case BA_REQID_GET_MORE_IN_SAME_SERIES:
        {
            DLog(@"BA_REQID_GET_MORE_IN_SAME_SERIES");
            //DLog(@"result: %@",result);

            BASearchResult* searchResult;
            @try {
                searchResult = [BASearchResult objectWithJSONObject:[result objectForKey:@"data"]];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(searchResult);
                
            }
            break;
        }

        case BA_REQID_GET_RELATED_LOANS:
        {
            DLog(@"BA_REQID_GET_RELATED_LOANS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSMutableDictionary *output = [NSMutableDictionary new];
            @try {
                for (NSString *key in [data allKeys]) {
                    BAStandardResultObject *resultObject = [BAStandardResultObject objectWithJSONObject:[data objectForKey:key]];
                    if (resultObject.result) {
                        resultObject.parsedData = [JSONEncodingHelperMethods parseArrayOfClass:[BARecord class] fromArray:resultObject.jsonData];
                        resultObject.jsonData = nil;
                    }
                    [output setObject:resultObject forKey:key];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(output);
                
            }
            break;
        }

        case BA_REQID_GET_ABOUT_CREATOR:
        {
            DLog(@"BA_REQID_GET_ABOUT_CREATOR");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSMutableDictionary *output = [NSMutableDictionary new];
            @try {
                for (NSString *key in [data allKeys]) {
                    // For each record identifier.
                    BAStandardResultObject *resultForIdentifier = [BAStandardResultObject objectWithJSONObject:[data objectForKey:key]];
                    if (resultForIdentifier.result) {
                        NSMutableDictionary *creators = [NSMutableDictionary new];
                        EXPECT_OBJECT(NSDictionary, resultForIdentifier.jsonData)
                        for (NSString *key in [resultForIdentifier.jsonData allKeys]) {
                            // For each creator.
                            BAStandardResultObject *resultForCreator = [BAStandardResultObject objectWithJSONObject:[resultForIdentifier.jsonData objectForKey:key]];
                            if (resultForCreator.result) {
                                // Parse BAAboutCreator object.
                                resultForCreator.parsedData = [BAAboutCreator objectWithJSONObject:resultForCreator.jsonData];
                                resultForCreator.jsonData = nil;
                            }
                            [creators setObject:resultForCreator forKey:key];
                        }
                        resultForIdentifier.parsedData = creators;
                        resultForIdentifier.jsonData = nil;
                    }
                    [output setObject:resultForIdentifier forKey:key];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(output);
                
            }
            break;
        }

        case BA_REQID_GET_REVIEWS:
        {
            DLog(@"BA_REQID_GET_REVIEWS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSMutableDictionary *output = [NSMutableDictionary new];
            @try {
                for (NSString *key in [data allKeys]) {
                    BAStandardResultObject *reviewsForItem = [BAStandardResultObject objectWithJSONObject:[data objectForKey:key]];
                    if (reviewsForItem.result) {
                        reviewsForItem.parsedData = [JSONEncodingHelperMethods parseArrayOfClass:[BAReview class] fromArray:reviewsForItem.jsonData];
                        reviewsForItem.jsonData = nil;
                    }
                    [output setObject:reviewsForItem forKey:key];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(output);
                
            }
            break;
        }

        case BA_REQID_GET_COVER_URLS:
        {
            DLog(@"BA_REQID_GET_COVER_URLS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSDictionary* resultDictionary;
            @try {
                //result is a dictionary of standard reults
                resultDictionary = [JSONEncodingHelperMethods parseDictionaryOfStringsToClass:[BAStandardResultObject class] fromJsonObject:data];
                
                //each standard result holds a BAAvaialbility object that must be parsed
                for (BAStandardResultObject* std_result_obj in [resultDictionary allValues]) {
                    [std_result_obj parseDataFieldToObjectOfClass:[BACoverUrl class]];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(resultDictionary);
                
            }
            break;
        }
            
        case BA_REQID_GET_RESERVATIONS:
        {
            DLog(@"BA_REQID_GET_RESERVATIONS");
            //DLog(@"result: %@",result);

            NSArray* resultArray;
            @try {
                resultArray = [JSONEncodingHelperMethods parseArrayOfClass:[BAReservation class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(resultArray);
                
            }
            break;
        }
            
        case BA_REQID_REMOVE_RESERVATIONS:
        {
            DLog(@"BA_REQID_REMOVE_RESERVATIONS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }
            
        case BA_REQID_ADD_RESERVATION:
        {
            DLog(@"BA_REQID_ADD_RESERVATION");
            DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }
            
        case BA_REQID_GET_AVAILABILITY:
        {
            DLog(@"BA_REQID_GET_AVAILABILITY");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSDictionary* resultDictionary;
            @try {
                //result is a dictionary of standard reults
                resultDictionary = [JSONEncodingHelperMethods parseDictionaryOfStringsToClass:[BAStandardResultObject class] fromJsonObject:data];
                
                //each standard result holds a BAAvaialbility object that must be parsed
                for (BAStandardResultObject* std_result_obj in [resultDictionary allValues]) {
                    [std_result_obj parseDataFieldToObjectOfClass:[BAAvailability class]];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(resultDictionary);
                
            }
            break;
        }

        case BA_REQID_GET_LOANS:
        {
            DLog(@"BA_REQID_GET_LOANS");
            //DLog(@"result: %@",result);

            NSArray* resultArray;
            @try {
                resultArray = [JSONEncodingHelperMethods parseArrayOfClass:[BALoan class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(resultArray);
                
            }
            break;
        }

        case BA_REQID_RENEW_LOANS:
        {
            DLog(@"BA_REQID_RENEW_LOANS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSDictionary* resultDictionary;
            @try {
                //result is a dictionary of standard reults
                resultDictionary = [JSONEncodingHelperMethods parseDictionaryOfStringsToClass:[BAStandardResultObject class] fromJsonObject:data];
                
                //each standard result holds a BAAvaialbility object that must be parsed
                for (BAStandardResultObject* std_result_obj in [resultDictionary allValues]) {
                    [std_result_obj parseDataFieldToObjectOfClass:[BALoan class]];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(resultDictionary); 
                
            }
            break;
        }

        case BA_REQID_RENEW_ALL_LOANS:
        {
            DLog(@"BA_REQID_RENEW_ALL_LOANS");
            //DLog(@"result: %@",result);

            NSDictionary* data = [result objectForKey:@"data"];
            NSDictionary* resultDictionary;
            @try {
                //result is a dictionary of standard reults
                resultDictionary = [JSONEncodingHelperMethods parseDictionaryOfStringsToClass:[BAStandardResultObject class] fromJsonObject:data];
                
                //each standard result holds a BAAvaialbility object that must be parsed
                for (BAStandardResultObject* std_result_obj in [resultDictionary allValues]) {
                    [std_result_obj parseDataFieldToObjectOfClass:[BALoan class]];
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(resultDictionary);
                
            }
            break;
        }

        case BA_REQID_GET_OPENING_HOURS:
        {
            DLog(@"BA_REQID_GET_OPENING_HOURS");
            //DLog(@"result: %@",result);

            NSArray *data = [result objectForKey:@"data"];
            NSArray *resultArray;
            @try {
                resultArray = [JSONEncodingHelperMethods parseArrayOfClass:[BAOpeningHoursLibrary class] fromArray:data];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock) {
                self.resultBlock(resultArray);
            }
            
            break;
        }

        case BA_REQID_GET_MORE_BY_CREATOR:
        {
            DLog(@"BA_REQID_GET_MORE_BY_CREATOR");
            //DLog(@"result: %@",result);

            BASearchResult* searchResult;
            @try {
                searchResult = [BASearchResult objectWithJSONObject:[result objectForKey:@"data"]];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(searchResult);
            }
            break;
        }
            
        case BA_REQID_LOAD_ALL_EVENTS:
        {
            DLog(@"BA_REQID_LOAD_ALL_EVENTS");
            //DLog(@"result: %@",result);

            NSArray *events;
            @try {
                events = [JSONEncodingHelperMethods parseArrayOfClass:[BAEvent class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object: %@",exception]);
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(events);
                
            }
            
            break;
        }

        case BA_REQID_LOAD_ALL_NEWS:
        {
            DLog(@"BA_REQID_LOAD_ALL_NEWS");
            //DLog(@"result: %@",result);

            NSArray *news;
            @try {
                news = [JSONEncodingHelperMethods parseArrayOfClass:[BANews class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object: %@",exception]);
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(news);
                
            }
            
            break;
        }
        case BA_REQID_GET_POPULAR:
        {
            DLog(@"BA_REQID_GET_POPULAR");
            DLog(@"result: %@",result);

            NSArray *popular;
            @try {
                popular = [JSONEncodingHelperMethods parseArrayOfClass:[BAPopular class] withKey:@"data" fromJsonObject:result];
            }
            @catch (NSException *exception) {
                self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object: %@",exception]);
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(popular);
                
            }
            
            break;
        }
        case BA_REQID_GET_OBJECT_BY_BARCODE_ID:
        {
            DLog(@"BA_REQID_GET_OBJECT_BY_BARCODE_ID");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            BAFullRecord* fullRecord;
            @try {
                fullRecord = [BAFullRecord objectWithJSONObject:data];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(fullRecord);
                
            }
            break;
        }
            
        case BA_REQID_GET_LIBRARY_NAMES:
        {
            DLog(@"BA_REQID_GET_LIBRARY_NAMES");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                EXPECT_OBJECT(NSDictionary, data);
            }
            
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(data);
                
            }
            break;
        }
            
        case BA_REQID_GET_LIBRARY_SETTINGS:
        {
            DLog(@"BA_REQID_GET_LIBRARY_SETTINGS");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            BALibrary* libraryObject;
            @try {
                libraryObject = [BALibrary objectWithJSONObject:data];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(libraryObject);
                
            }
            break;
        }
            
        case BA_REQID_IS_SUPPORTED:
        {
            DLog(@"BA_REQID_IS_SUPPORTED");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            BASupportInfo* supportInfoObject;
            @try {
                supportInfoObject = [BASupportInfo objectWithJSONObject:data];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(supportInfoObject);
                
            }
            break;
        }

        //FIXME: move to separate file
            
#ifdef BA_LIBRARIAN_BUILD
        case LB_REQID_GET_OBJECT_ITEMS:
        {
            DLog(@"BA_REQID_GET_OBJECT_ITEMS");
            DLog(@"result: %@",result);
            
            NSArray* data = [result objectForKey:@"data"];
            NSArray* result;
            @try {
               result = [JSONEncodingHelperMethods parseArrayOfClass:[LBHoldingItem class] fromArray:data];
               EXPECT_OBJECT(NSArray, result);
            }
            
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(result);
                
            }
            break;
        }

        case LB_REQID_GET_OBJECT_ITEMS_HISTORY:
        {
            DLog(@"BA_REQID_GET_OBJECT_ITEMS_HISTORY");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            NSDictionary* resultDictionary;

            @try {
                //result is a dictionary of standard reults
                resultDictionary = [JSONEncodingHelperMethods parseDictionaryOfStringsToClass:[BAStandardResultObject class] fromJsonObject:data];
                
                //each standard result holds a BAAvaialbility object that must be parsed
                for (BAStandardResultObject* std_result_obj in [resultDictionary allValues]) {
                    [std_result_obj parseDataFieldToObjectOfClass:[LBHoldingItemHistoryRecord class]];
                }
            }
            
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(resultDictionary);
                
            }
            break;
        }

        case LB_REQID_ADD_RESERVATION_FOR_PATRON:
        {
            DLog(@"LB_REQID_ADD_RESERVATION_FOR_PATRON");
            DLog(@"result: %@",result);
            
            NSDictionary* data = [result objectForKey:@"data"];
            @try {
                NSLog(@"Needs parsing: %@",data); //FIXME
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            [BARPCManager updateAuthenticatedSessionTimestamp];
            if (self.resultBlock != nil) {
                self.resultBlock(data); //FIXME
                
            }
            break;
        }
            
            
            
        case LB_REQID_LIBRARIAN_SEARCH:
        {
            DLog(@"LB_REQID_LIBRARIAN_SEARCH");
            //DLog(@"result: %@",result);
            
            BASearchResult* searchResult;
            @try {
                searchResult = [BASearchResult objectWithJSONObject:[result objectForKey:@"data"]];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(searchResult);
                
            }
            break;
        }
            
        case LB_REQID_GET_AUTOCOMPLETION_LIST:
        {
            DLog(@"LB_REQID_GET_AUTOCOMPLETION_LIST");
            //DLog(@"result: %@",result);
            
            LBAutoCompletionListContainer* completionListResult;
            @try {
                completionListResult = [LBAutoCompletionListContainer objectWithJSONObject:[result objectForKey:@"data"]];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@",exception]);
                }
                break;
            }
            if (self.resultBlock != nil) {
                self.resultBlock(completionListResult);
                
            }
            break;
        }
#endif

#ifdef BA_IMF_BUILD
        // -------------- IMF methods ----------------------------------------
            
            
        case IMF_REQID_CREATE_NEW_PACKAGE_ID:
        {
            DLog(@"IMF_REQID_CREATE_NEW_PACKAGE_ID")
            BAStandardResultObject* standardObject;
            NSString* newPackageId;
            @try {
                standardObject = [BAStandardResultObject objectWithJSONObject:result];
                if (standardObject.result) {
                    newPackageId = standardObject.jsonData;
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(newPackageId);
            }
            break;
        }
        case IMF_REQID_UPDATE_PACKAGE_ID:
        {
            DLog(@"IMF_REQID_UPDATE_PACKAGE_ID")
            
            BAStandardResultObject* standardObject;
            @try {
                standardObject = [BAStandardResultObject objectWithJSONObject:result];
                BOOL resultCode = standardObject.result;
                if(resultCode) {
                    DLog(@"Update package succeded");
                } else {
                    DLog(@"Update package failed");
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(standardObject);
            }
            break;
        }
        case IMF_REQID_DELETE_PACKAGE:
        {
            DLog(@"IMF_REQID_DELETE_PACKAGE")
            
            BAStandardResultObject* standardObject;
            @try {
                standardObject = result;
                BOOL resultCode = [[result objectForKey:@"result"] boolValue];
                if(resultCode) {
                    DLog(@"Delete package succeded");
                } else {
                    DLog(@"Delete package package failed");
                }
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(standardObject);
            }
            break;
        }
        case IMF_REQID_GET_PACKAGE:
        {
            DLog(@"IMF_REQID_GET_PACKAGE")
            
            NSDictionary* resultDictionary;
            
            IMFPackage* package;
            @try {
                resultDictionary = [result objectForKey:@"data"];
                package = [IMFPackage objectWithJSONObject:resultDictionary];
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(package);
            }
            break;
        }
        case IMF_REQID_GET_PACKAGE_LIST:
        {
            DLog(@"IMF_REQID_GET_PACKAGE_LIST")
            NSDictionary* resultDictionary;
            NSArray* resultPackageListArray;
            NSInteger resultCount;
            IMFPackageListResult* resultObject;
            @try {
                resultDictionary = [result objectForKey:@"data"];
                resultPackageListArray = [resultDictionary objectForKey:@"packages"];
                resultCount = [[resultDictionary objectForKey:@"count"] integerValue];
                resultObject = [IMFPackageListResult new];
                
                NSMutableArray* packagesInList = [NSMutableArray new];
                for(NSDictionary* abstractPackageAsJson in resultPackageListArray) {
                    [packagesInList addObject:[IMFAbstractPackage objectWithJSONObject:abstractPackageAsJson]];
                }
                resultObject.packageCount = resultCount;
                resultObject.packageList = packagesInList;
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(resultObject);
            }
            break;
        }
        case IMF_REQID_GET_SUGGESTED_PACKAGES:
        {
            DLog(@"IMF_REQID_GET_SUGGESTED_PACKAGES")
            NSArray* resultPackageListArray;
            NSArray* resultObject;
            @try {
                resultPackageListArray = [result objectForKey:@"data"];
                NSMutableArray* packagesInList = [NSMutableArray new];
                for(NSDictionary* packageAsJson in resultPackageListArray) {
                    [packagesInList addObject:[IMFPackage objectWithJSONObject:packageAsJson]];
                }
                resultObject = packagesInList;
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(resultObject);
            }
            break;
        }
        case IMF_REQID_DELETE_IMAGE_PACKAGE:
        {
            DLog(@"IMF_REQID_DELETE_IMAGE_PACKAGE")
            NSLog(@"FIXME, not impl"); //FIXME: not impl
            break;
        }
        case IMF_REQID_SEARCH_PACKAGES:
        {
            DLog(@"IMF_REQID_SEARCH_PACKAGES")
            NSDictionary* resultDictionary;
            NSArray* resultPackageListArray;
            NSInteger resultCount;
            IMFPackageListResult* resultObject;
            @try {
                resultDictionary = [result objectForKey:@"data"];
                resultPackageListArray = [resultDictionary objectForKey:@"packages"];
                resultCount = [[resultDictionary objectForKey:@"count"] integerValue];
                resultObject = [IMFPackageListResult new];
                
                NSMutableArray* packagesInList = [NSMutableArray new];
                for(NSDictionary* abstractPackageAsJson in resultPackageListArray) {
                    [packagesInList addObject:[IMFAbstractPackage objectWithJSONObject:abstractPackageAsJson]];
                }
                resultObject.packageCount = resultCount;
                resultObject.packageList = packagesInList;
            }
            @catch (NSException *exception) {
                if (self.errorBlock != nil) {
                    self.errorBlock(BA_RPC_ERROR_SERVER_RETURNED_UNEXPECTED_OBJECT, [NSString stringWithFormat:@"Server returned unexpected object, %@", exception]);
                }
                break;
            }
            if(self.resultBlock != nil) {
                self.resultBlock(resultObject);
            }
            break;
        }
#endif
        
        default:
            DLog(@"Error: %@", @"Unknown response");
            if (self.errorBlock != nil) {
                self.errorBlock(BA_RPC_ERROR_UNKNOWN_RESPONSE, @"Unknown response");
            }
            break;
    }

}

- (void)JSONRPCClient:(JSONRPCClient *)client requestFailed:(NSInteger)requestId code:(NSInteger)code message:(NSString *)message data:(id)data
{
    [BARPCManager removeConnection:client];

    NSString* formatted_message = [NSString stringWithFormat:@"BApps RPC error: %@, code %d", message, code];
    NSLog(@"%@", formatted_message);
    if (self.errorBlock != nil) {
        NSString* localizedError = formatted_message;
        
        switch (code) {
            case JSONRPC_NETWORKERROR:
                localizedError = @"Netværksfejl - kontroller dine netværksindstillinger og prøv igen.";
                break;
            case JSONRPC_PARSEERROR:
                localizedError = @"Ukendt serverfejl - prøv venligst igen senere.";
                break;
                
            case JSONRPC_SERVERERROR:
                localizedError = @"Intern serverkommunikationsfejl - prøv venligst igen senere.";
                break;
                
            case JSONRPC_UNKNOWNERROR:
                localizedError = @"En ukendt fejl opstod under kommunikation med netværket. Prøv venligst igen senere.";
                break;
               
            default:
                break;
        }
        self.errorBlock(BA_RPC_ERROR_REQUEST_FAILED, localizedError);
    }
}

- (void)JSONRPCClient:(JSONRPCClient *)client requestDoneRaw:(NSData *)responseData
{
    //more debugging
    //DLog(@"raw json: %@",[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
}
@end
